package be.kuleuven.cs.mop.domain.model.impl;


import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import be.kuleuven.cs.mop.domain.exceptions.TaskManagerException;
import be.kuleuven.cs.mop.domain.model.Task;
import be.kuleuven.cs.mop.domain.model.TaskStatus;
import be.kuleuven.cs.mop.domain.model.User;



/**
 * Internal implementation of the {@link User} interface
 */
public class UserImpl extends WorldElement implements User {
	
	private final String		name;
	private final Set<TaskImpl>	tasks	= new HashSet<TaskImpl>();
	private final UserTypeImpl	type;
	
	
	/**
	 * Creates a new user and adds it to the world
	 * @param world - a world
	 * @param name - a name
	 * 
	 * @throws TaskManagerException USER_INVALID_NAME - !{@link UserImpl#isValidName(String)}
	 * @throws TaskManagerException INVALID_USER_TYPE - !{@link UserImpl#isValidUserType(UserTypeImpl)}
	 */
	public UserImpl(final World world, final String name, final UserTypeImpl type) throws TaskManagerException {
		super(world);
		if (!isValidName(name))
			throw new TaskManagerException(TaskManagerException.USER_INVALID_NAME);
		if(!isValidUserType(type))
			throw new TaskManagerException(TaskManagerException.INVALID_USER_TYPE);
		
		this.type = type;
		this.name = name;
		
		getWorld().add(this);
	}
	
	
	
	/**
	 * Creates a task and adds the created task to this user
	 * @see TaskImpl#TaskImpl(UserImpl, Schedule, Set, HashMap, TaskTypeImpl)
	 * @Note: Creator pattern
	 */
	public TaskImpl createTask(Schedule schedule,
			final Set<TaskImpl> dependencies,
			final HashMap<String,Field> fields, final TaskTypeImpl type) throws TaskManagerException {
		final TaskImpl task = new TaskImpl(this,
				schedule, dependencies, fields, type);
		tasks.add(task);
		return task;
	}
	
	/**
	 * 'Hidden' factory method used by the XML parser
	 * 
	 * @see {@link UserImpl#createTask(Schedule, Set, HashMap, TaskTypeImpl)}
	 */
	public TaskImpl createTaskInitial(final Schedule schedule, final Set<TaskImpl> dependencies,
			final HashMap<String, Field> fields, final TaskTypeImpl type) throws TaskManagerException {
		final TaskImpl task = new TaskImpl(this,
				schedule, dependencies, fields, type, true);
		tasks.add(task);
		return task;
	}
	
	/**
	 * Returns a <code>Calendar</code> representing the end of the current day
	 */
	protected Calendar getFocusDeadline() {
		final Calendar today = new GregorianCalendar();
		today.set(Calendar.HOUR_OF_DAY,	23);
		today.set(Calendar.MINUTE,		59);
		return today;
	}
	
	/**
	 * Returns the name of this user
	 */
	@Override
	public String getName() { return name; }
	
	/**
	 * Returns an unmodifiable <code>Set</code> with all {@link Task}s of this {@link User}
	 */
	public Set<TaskImpl> getTasks() { return Collections.unmodifiableSet(tasks); }
	
	/**
	 * Returns a <code>List</code> of all the unfinished {@link Task}s
	 * 	for this {@link User}
	 */
	public List<TaskImpl> getTasksUnfinished() {
		final ArrayList<TaskImpl> tasks = new ArrayList<TaskImpl>();
		for (TaskImpl task : getTasks() )
			if (task.getStatus().equals(TaskStatus.UNFINISHED))
				tasks.add(task);
		return tasks;
	}
	
	/**
	 * Returns the usertype of this user
	 */
	@Override
	public UserTypeImpl getType() { return type; }
	
	/**
	 * Removes this task from the list of tasks from this user
	 * @param task - The task to be removed
	 */
	public void remove(final TaskImpl task) {
		tasks.remove(task);
	}
	
	@Override
	public String toString() {
		return new StringBuilder(64)
		.append("User[name='").append(name).append("']")
		.toString();
	}
	
	
	/**
	 * Returns true if this user is an admin user
	 */
	@Override
	public boolean isAdmin(){
		return getType().isAdmin();
	}
	
	
	/**
	 * Validates a given name
	 * @param name - the given name
	 * @return true when the name is not already in use, not null and not empty
	 */
	private boolean isValidName(String name){
		return ((name != null) && !name.isEmpty() && !userAlreadyExists(name));
	}
	
	
	/**
	 * Checks whether a given username is already in use by an earlier created user
	 * @param name - name of given user
	 * @return false if username is already in use by an earlier created user
	 */
	private boolean userAlreadyExists(String name) {
		for(UserImpl user : getWorld().getUsers()) {
			if(user.getName().equals(name))
				return true;
		}
		return false;
	}


	/**
	 * Validates a given usertype.
	 * @param type - the given usertype
	 * @return false if type is null or not present in the system
	 */
	private boolean isValidUserType(UserTypeImpl type) {
		return type != null && getWorld().isPresent(type);
	}
	
	/**
	 * Returns a list of the tasks this user is invited on
	 */
	public List<TaskImpl> getInvitedTasks() {
		return getWorld().getInvitationManager().getInvitedTasks(this);
	}
	
}